//=================================================================================
//    COPYRIGHT(C) Innobeam
//    ALL RIGHTS RESERVED
//=================================================================================
//Filename    : lan9252csr_proc.v rev 1.0
//Created On  : 2017-09-05
//Author      : shilong.zhang
//Description :This module access the LAN9252 CSR;
// through HBI bus.
//Include     :
//Modification:
//=================================================================================
module lan9252csr_proc
(
	iClk   		,//input clk
	iRst_n  	,//reset (low active)

	ivData		,//input 32bit data,to be written to CSR
	ivAddr  	,//input 16bit address,the destination address of CSR
	iWrEn		,//input CSR write enable
	iRdEn   	,//input CSR read enable
	ovData  	,//output data read from CSR
	oDone   	,//operation done signal

	//lan9252_interface module interface
	ovLanData 	,//32bit data to lan9252_interface module
	ovLanAddr 	,//16bit address to lan9252_interface module
	ivLanData 	,//32bit data from lan9252_interface module
	oLanWr    	,//write enable to lan9252_interface module
	oLanRd    	,//read enable to lan9252_interface module
	iLanDone	//input operation done from lan9252_interface module
);
//=================================================================================
//    parameter
//=================================================================================
//data width define
parameter	DATA_W = 16;

//csr reg fixed address and cmd
parameter	CSR_CMD_ADR = 16'h0182;	//the address of CSR CMD register
parameter	CSR_DAT_ADR = 16'h0180;	//the address of CSR DATA register
parameter   CSR_CMD_WR = 16'h8004; //CMD of write 4 bytes
parameter   CSR_CMD_RD = 16'hC004; //CMD of read 4 bytes

//state machine
parameter IDLE = 4'h0000;		//IDLE state
parameter WR_CSRCMD = 4'b0001;	//write CSR CMD state
parameter RD_CSRCMD = 4'b0010;	//read CSR CMD state
parameter WR_CSRDAT = 4'b0011;	//write CSR DATA state
parameter RD_CSRDAT = 4'b0100;	//read CSR DATA state

//=================================================================================
//    port
//=================================================================================
input 							iClk,iRst_n;
input 		[2*DATA_W-1:0]		ivData;
input 		[DATA_W-1:0]		ivAddr;
input 							iWrEn,iRdEn;
output 		[2*DATA_W-1:0]		ovData;
output							oDone;

//lan9252_interface module interface
output		[2*DATA_W-1:0]		ovLanData; //32bit data to lan9252_interface module
output		[DATA_W-1:0]		ovLanAddr;	//16bit address to lan9252_interface module
input		[2*DATA_W-1:0]		ivLanData; //32bit data from lan9252_interface module
output							oLanWr;
output							oLanRd;
input							iLanDone;

//=================================================================================
//    signal
//=================================================================================
reg		[DATA_W-1:0] 		oLan9252Ad_reg;	//Lan9252 16bit address and data bus register
reg 						oLanWr,oLanRd;	//lan9252 write enable,read enable register
reg 						oDone;			//lan9252 CSR operation done register
reg							rLanWrOrRdN;	//flag register,1:write; 0:read;

reg 	[3:0]				rvState_c;		//current state register
reg 	[3:0]				rvState_n;		//next state register
reg 	[DATA_W-1:0]  		rvAddr;			//address output register
reg 	[DATA_W-1:0]  		rvDestAddr;		//Register for temporarily storing the
											//destination CSR address
reg 	[2*DATA_W-1:0] 		rvData;			//data output register
reg 	[2*DATA_W-1:0] 		rvRLandata;		//store the data read from CSR
reg 	[2*DATA_W-1:0] 		rvDataBuf;		//Temporarily store the data to be
											//written to the CSR register
reg							rWtFlag;		//state flag. 0:IDLE,write or read operation
											//is not enabled. 1:WAIT,write or read operation is
											//enabled,wait for result.

//state machine state change option
wire 	wIdle2wr_csrcmd_start ;		//IDLE state to write CSR CMD state flag
wire 	wWr_csrcmd2rd_csrcmd_start ;//write CSR CMD state to read CSR CMD state flag
wire 	wRd_csrcmd2rd_csrdat_start ;//read CSR CMD state to read CSR DATA state flag
wire 	wRd_csrdat2idle_start ;		//read CSR DATA state to IDLE state flag
wire 	wIdle2wr_csrdat_start ;		//IDLE to Write CSR DATA state flag
wire 	wWr_csrdat2wr_csrcmd_start ;//write CSR DATA state to write CSR CMD state flag
wire 	wRd_csrcmd2idle_start;		//read CSR CMD state to IDLE state flag


//===========================================================================
// State machine description:
// IDLE 		wIdle2wr_csrcmd_start == 1 			-> WR_CSRCMD
// IDLE 		wIdle2wr_csrdat_start == 1 			-> WR_CSRDAT
// WR_CSRCMD 	wWr_csrcmd2rd_csrcmd_start == 1 	-> RD_CSRCMD
// RD_CSRCMD 	wRd_csrcmd2rd_csrdat_start == 1 	-> RD_CSRDAT
// RD_CSRCMD 	wRd_csrcmd2idle_start == 1 			-> IDLE
// WR_CSRDAT	wWr_csrdat2wr_csrcmd_start == 1		-> WR_CSRCMD
// RD_CSRDAT	wRd_csrdat2idle_start == 1			-> IDLE
//===========================================================================

//first block: synchronous timing always module, format description of
//the next state register migration to the current state register (no need to change)
always@(posedge iClk or negedge iRst_n)begin
	if(!iRst_n)begin
		rvState_c <= IDLE;
		end
	else begin
		rvState_c <= rvState_n;
		end
	end

//2nd block: combination logic always module, describe the state transition condition judgment
always@(*)begin
case(rvState_c)
IDLE:begin
		if(wIdle2wr_csrcmd_start)begin
			rvState_n = WR_CSRCMD;
			end
		else if(wIdle2wr_csrdat_start)begin
			rvState_n = WR_CSRDAT;
			end
		else begin
			rvState_n = rvState_c;
			end
		end
WR_CSRCMD:begin
		if(wWr_csrcmd2rd_csrcmd_start)begin
			rvState_n = RD_CSRCMD;
			end
		else begin
			rvState_n = rvState_c;
			end
		end
RD_CSRCMD:begin
		if(wRd_csrcmd2rd_csrdat_start)begin
			rvState_n = RD_CSRDAT;
			end
		else if(wRd_csrcmd2idle_start)begin
			rvState_n = IDLE;
			end
		else begin
			rvState_n = rvState_c;
			end
		end
WR_CSRDAT:begin
		if(wWr_csrdat2wr_csrcmd_start)begin
			rvState_n = WR_CSRCMD;
			end
		else begin
			rvState_n = rvState_c;
			end
		end
RD_CSRDAT:begin
		if(wRd_csrdat2idle_start)begin
			rvState_n = IDLE;
			end
		else begin
			rvState_n = rvState_c;
			end
		end

default:begin
	rvState_n = IDLE;
	end
endcase
end

//3rd block：design transfer conditions
assign wIdle2wr_csrcmd_start = (rvState_c == IDLE) && (iRdEn == 1'b1);
assign wWr_csrcmd2rd_csrcmd_start = (rvState_c == WR_CSRCMD) && (iLanDone == 1'b1);
assign wRd_csrcmd2rd_csrdat_start = (rvState_c == RD_CSRCMD) && (iLanDone == 1'b1)
 && (ivLanData[31] == 1'b0) && (rLanWrOrRdN == 1'b0);
assign wRd_csrdat2idle_start = (rvState_c == RD_CSRDAT) && (iLanDone == 1'b1);
assign wIdle2wr_csrdat_start = (rvState_c == IDLE) && (iWrEn == 1'b1) ;
assign wWr_csrdat2wr_csrcmd_start = (rvState_c == WR_CSRDAT) && (iLanDone == 1'b1);
assign wRd_csrcmd2idle_start = (rvState_c == RD_CSRCMD) && (iLanDone == 1'b1)
 && (ivLanData[31] == 1'b0) && (rLanWrOrRdN == 1'b1);

//4th: synchronous timing always module, format description register output
//(may have more than one output)
assign ovData = rvRLandata;
assign ovLanData = rvData;
assign ovLanAddr = rvAddr;

always  @(posedge iClk or negedge iRst_n)begin
if(!iRst_n)begin
	rvAddr <= 16'd0;
	rvDestAddr <= 16'd0;
	rvData <= 32'd0;
	rvRLandata <= 32'd0;
	oDone <= 1'b0;
	oLanWr <= 1'b0;
	oLanRd <= 1'b0;
	rLanWrOrRdN <= 1'b0;
	rvDataBuf <= 32'd0;
	rWtFlag <= 1'b0;
	end
else begin
case(rvState_c)
IDLE:begin
	oLanWr <= 1'b0;
	oDone <= 1'b0;
	rWtFlag <= 1'b0;
	rvData <= rvData;
	rvRLandata <= rvRLandata;
	oLanRd <= oLanRd;
	if(iWrEn) begin//receive write enable signal
		rvAddr <= rvAddr;
		rvDestAddr <= ivAddr;	//store destination CSR address
		rvDataBuf <= ivData;	//store input data to be written
		rLanWrOrRdN <= 1'b1;	//means this is a write operation
		end
	else if(iRdEn) begin
		rvAddr <= rvAddr;
		rvDestAddr <= ivAddr;	//store destination CSR address
		rvDataBuf <= ivData;    //not used
		rLanWrOrRdN <= 1'b0;    //means this is a read operation
		end
	else begin					//not changed
		rvAddr <= rvAddr;
		rvDestAddr <= rvDestAddr;
		rvDataBuf <= rvDataBuf;
		rLanWrOrRdN <= 1'b0;
		end
	end
WR_CSRCMD:begin	//write CSR CMD
	oDone <= 0;
	rvDestAddr <= rvDestAddr;
	rvRLandata <= rvRLandata;
	oLanRd <= oLanRd;
	rLanWrOrRdN <= rLanWrOrRdN;
	rvDataBuf <= rvDataBuf;
	if(rWtFlag==1'b0) begin	//if write enable or read enable is in IDLE
		oLanWr <= 1'b1;		//write enable to lan9252_interface module
		rvAddr <= CSR_CMD_ADR;//;	//write address is CSR_CMD_ADR
		rWtFlag <= 1'b1;			//change to WAIT state
		if(rLanWrOrRdN == 1'b1)begin//a write operation
			rvData <= {CSR_CMD_WR,rvDestAddr};//write cmd and address
			end
		else begin
			rvData <= {CSR_CMD_RD,rvDestAddr};//read cmd and address
			end
		end
	else if(iLanDone == 1'b1) begin//in WAIT state if receive iLanDone
		rWtFlag <= 1'b0;	//change to IDLE state
		oLanWr <= 1'b0;		//write enable clear
		rvData <= rvData;
		rvAddr <= rvAddr;
		end
	else begin
		oLanWr <= 1'b0;
		rvData <= rvData;
		rvAddr <= rvAddr;
		rWtFlag <= rWtFlag;
		end

	end
RD_CSRCMD:begin
	rvDestAddr <= rvDestAddr;
	rvData <= rvData;
	rvRLandata <= rvRLandata;
	oLanWr <= oLanWr;
	rLanWrOrRdN <= rLanWrOrRdN;
	rvDataBuf <= rvDataBuf;
	if(rWtFlag==1'b0) begin
		oLanRd <= 1'b1;
		rvAddr <= CSR_CMD_ADR;//;
		rWtFlag <= 1'b1;
		oDone <= 1'b0;
		end
	else if(iLanDone == 1'b1) begin
		rWtFlag <= 1'b0;
		if(rLanWrOrRdN == 1'b1 && ivLanData[31] == 1'b0)begin
			oDone <= 1'b1;
			end
		else begin
			oDone <= 1'b0;
			end
		end
	else begin
		rWtFlag <= rWtFlag;
		oDone <= 1'b0;
		rvAddr <= rvAddr;
		oLanRd <= 1'b0;
		end
	end
WR_CSRDAT:begin
	rvDestAddr <= rvDestAddr;
	oDone <= 0;
	rvRLandata <= rvRLandata;
	oLanRd <= oLanRd;
	rLanWrOrRdN <= rLanWrOrRdN;
	rvDataBuf <= rvDataBuf;
	if(rWtFlag==1'b0) begin
		oLanWr <= 1'b1;
		rvAddr <= CSR_DAT_ADR;
		rvData <= rvDataBuf;
		rWtFlag <= 1'b1;
		end
	else if(iLanDone == 1'b1) begin
		rWtFlag <= 1'b0;
		oLanWr <= 1'b0;
		rvData <= rvData;
		rvAddr <= rvAddr;
		end
	else begin
		oLanWr <= 1'b0;
		rvData <= rvData;
		rvAddr <= rvAddr;
		rWtFlag <= rWtFlag;
		end
	end
RD_CSRDAT:begin
	rvDestAddr <= rvDestAddr;
	rvData <= rvData;
	oLanWr <= oLanWr;
	rLanWrOrRdN <= rLanWrOrRdN;
	rvDataBuf <= rvDataBuf;
	if(rWtFlag==1'b0) begin
		oLanRd <= 1'b1;
		rvAddr <= CSR_DAT_ADR;
		rWtFlag <= 1'b1;
		rvRLandata <= rvRLandata;
		oDone <= 1'b0;
		end
	else if(iLanDone == 1'b1) begin
		rvRLandata <= ivLanData;
		rWtFlag <= 1'b0;
		rvAddr <= rvAddr;
		oLanRd <= 1'b0;
		if(rLanWrOrRdN == 1'b0)begin
			oDone <= 1'b1;
			end
		else begin
			oDone <= 1'b0;
			end
		end
	else begin
		rWtFlag <= rWtFlag;
		rvRLandata <= rvRLandata;
		oDone <= 1'b0;
		oLanRd <= 1'b0;
		rvAddr <= rvAddr;
		end
	end

default:begin
	oLanWr <= 1'b0;
	rvAddr <= rvAddr;
	rvDestAddr <= rvDestAddr;
	rvData <= rvData;
	rvRLandata <= rvRLandata;
	oDone <= 1'b0;
	rWtFlag <= rWtFlag;
	oLanRd <= oLanRd;
	rLanWrOrRdN <= rLanWrOrRdN;
	rvDataBuf <= rvDataBuf;
	end
	endcase
	end
end

//================================END=======================================

endmodule

